package com.sinitek.web.exception;

import com.sinitek.enums.ApiResultEnum;
import com.sinitek.web.response.ApiResult;
import com.sinitek.web.response.R;
import lombok.extern.log4j.Log4j2;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.Set;

/**
 * @program: elastic
 * @description: 异常处理
 * @author: Mr.chen
 * @create: 2019-04-24 12:47
 */
@RestControllerAdvice
@Log4j2
public class ExceptionHandler {

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @org.springframework.web.bind.annotation.ExceptionHandler(MissingServletRequestParameterException.class)
    public ApiResult handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        log.error("缺少请求参数:{}", e);
        return R.error(ApiResultEnum.PARAM_NOT_COMPLETE.getCode(), ApiResultEnum.PARAM_NOT_COMPLETE.getMsg());
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @org.springframework.web.bind.annotation.ExceptionHandler(HttpMessageNotReadableException.class)
    public ApiResult handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error("缺少请求参数:{}", e);
        return R.error(ApiResultEnum.PARAM_NOT_COMPLETE.getCode(), ApiResultEnum.PARAM_NOT_COMPLETE.getMsg());
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @org.springframework.web.bind.annotation.ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        FieldError error = bindingResult.getFieldError();
        String field = error.getField();
        String code = error.getDefaultMessage();
        String message = String.format("%s:%s", field, code);
        log.error("方法参数类型错误:{}", message);
        return R.error(ApiResultEnum.METHOD_TYPE_BIND_ERROR.getCode(), ApiResultEnum.METHOD_TYPE_BIND_ERROR.getMsg());
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @org.springframework.web.bind.annotation.ExceptionHandler(BindException.class)
    public ApiResult handleBindException(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        FieldError error = bindingResult.getFieldError();
        String field = error.getField();
        String code = error.getDefaultMessage();
        String message = String.format("%s:%s", field, code);
        log.error("参数类型错误:{}", message);
        return R.error(ApiResultEnum.PARAM_TYPE_BIND_ERROR.getCode(), ApiResultEnum.PARAM_TYPE_BIND_ERROR.getMsg());
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @org.springframework.web.bind.annotation.ExceptionHandler(ConstraintViolationException.class)
    public ApiResult handleServiceException(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        ConstraintViolation<?> violation = violations.iterator().next();
        String message = violation.getMessage();
        log.error("参数无效:{}", message);
        return R.error(ApiResultEnum.PARAM_IS_INVALID.getCode(), ApiResultEnum.PARAM_IS_INVALID.getMsg());
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @org.springframework.web.bind.annotation.ExceptionHandler(ValidationException.class)
    public ApiResult handleValidationException(ValidationException e) {
        log.error("参数无效:{}", e);
        return R.error(ApiResultEnum.PARAM_IS_INVALID.getCode(), ApiResultEnum.PARAM_IS_INVALID.getMsg());
    }

    /**
     * 405 - Method Not Allowed
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @org.springframework.web.bind.annotation.ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ApiResult handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error("不允许的请求方式:{}", e);
        return R.error(ApiResultEnum.METHOD_NOT_ALLOW.getCode(), ApiResultEnum.METHOD_NOT_ALLOW.getMsg());
    }

    /**
     * 415 - Unsupported Media Type
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @org.springframework.web.bind.annotation.ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public ApiResult handleHttpMediaTypeNotSupportedException(Exception e) {
        log.error("不支持当前媒体类型:{}", e);
        return R.error(ApiResultEnum.UNSUPPORTED_MEDIA_TYPE.getCode(), ApiResultEnum.UNSUPPORTED_MEDIA_TYPE.getMsg());
    }

    /**
     * 502 - Bad Gateway
     */
    @ResponseStatus(HttpStatus.BAD_GATEWAY)
    @org.springframework.web.bind.annotation.ExceptionHandler(IllegalArgumentException.class)
    public ApiResult handleArgumentException(IllegalArgumentException e) {
        log.error("非法参数异常", e);
        return R.error(ApiResultEnum.ILLEGAL_ARGUMENT.getCode(), ApiResultEnum.ILLEGAL_ARGUMENT.getMsg());
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.OK)
    @org.springframework.web.bind.annotation.ExceptionHandler(Exception.class)
    public ApiResult handleException(Exception e) {
        log.error("系统异常:{}", e);
        return R.error(ApiResultEnum.SYSTEM_ERR.getCode(), ApiResultEnum.SYSTEM_ERR.getMsg());
    }


    /**
     * 操作数据库出现异常:名称重复，外键关联
     */
    @ResponseStatus(HttpStatus.OK)
    @org.springframework.web.bind.annotation.ExceptionHandler(DataIntegrityViolationException.class)
    public ApiResult handleException(DataIntegrityViolationException e) {
        log.error("操作数据库出现异常:{}", e);
        return R.error(ApiResultEnum.DATABASE_OPERATE_ERROR.getCode(), ApiResultEnum.DATABASE_OPERATE_ERROR.getMsg());
    }

}
